﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.Text;
using Zhao.Common.Helper;
using Zhao.Common.JWT;

namespace Zhao.Arena.MiniProgram.Api.Utility.AuthorizationExt
{
    /// <summary>
    /// 授权认证
    /// </summary>
    public static class AuthorizationRegister
    {
        /// <summary>
        /// 支持授权的扩展方法
        /// </summary>
        /// <param name="builder"></param>
        public static void AuthorizationExt(this WebApplicationBuilder builder)
        {
            JWTTokenOptions tokenOptions = new JWTTokenOptions();
            builder.Configuration.Bind("JwtSettings", tokenOptions);

            #region 配置jwt指定编码的统一返回值 避免直接返回
            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//Scheme
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        //JWT有一些默认的属性，就是给鉴权时就可以筛选了
                        ValidateIssuer = true,//是否验证Issuer
                        ValidateAudience = false,//是否验证Audience
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidIssuer = tokenOptions.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.SecurityKey!)),
                        ClockSkew = TimeSpan.FromSeconds(0),//设置token过期后多久失效，默认过期后300秒内仍有效 
                    };

                    options.Events = new JwtBearerEvents
                    {
                        //此处为权限验证失败后的执行的委托，涵盖的场景：
                        //1、没有token的
                        //2、token错误的
                        //3、Token过期的
                        OnChallenge = context =>
                        {
                            //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                            context.HandleResponse();
                            //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                            var payload = JsonConvert.SerializeObject(new ApiResult()
                            {
                                Success = false,
                                Message = "对不起没有授权，没有Token",
                                StatusCode = 401
                            }, new JsonSerializerSettings
                            {
                                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                            });
                            //自定义返回的数据类型
                            context.Response.ContentType = "application/json";
                            //自定义返回状态码，默认为401 我这里改成 200
                            context.Response.StatusCode = StatusCodes.Status200OK;
                            //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            //输出Json数据结果
                            context.Response.WriteAsync(payload);
                            return Task.FromResult(0);
                        },


                        //此处为权限校验失败后的执行的委托；涵盖的场景：
                        //1.带的有token，token是有效期内的，通过一些逻辑计算后，发现还是没有权限；
                        OnForbidden = context =>
                        {
                            //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                            string payload = JsonConvert.SerializeObject(new ApiResult()
                            {
                                Success = false,
                                Message = "对不起，您不具备访问该功能的权限，token是对的，token也在有效期内",
                                StatusCode = 403
                            }, new JsonSerializerSettings
                            {
                                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                            });


                            //自定义返回的数据类型
                            context.Response.ContentType = "application/json";
                            //自定义返回状态码，默认为403 我这里改成 200
                            context.Response.StatusCode = StatusCodes.Status200OK;
                            //context.Response.StatusCode = StatusCodes.Status403Forbidden;
                            //输出Json数据结果
                            context.Response.WriteAsync(payload);
                            return Task.FromResult(0);
                        },

                    };
                });
            #endregion

            #region 一些jwt的额外扩展比如说指定认证 和细粒度的权限控制和一些自定义的业务逻辑验证
            //builder.Services.AddAuthorization(options =>
            //{
            //        //1. 添加策略      策略名称 在控制器或者方法声明的时候要添加上
            //        options.AddPolicy("btnPolicy", policyBuilder => policyBuilder
            //        //认证策略 这里使用的是 jwt的认证策略
            //        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme));
            //        //.AddRequirements(new MenuAuthorizeRequirement()));  //2. 需要自定义策略
            //});
            #endregion
        }
    }
}
